﻿using System;
using DC2010.Objects;
using DC2010.Objects.Data;
using DC2010.Systems;
using DC2010.Objects.Base;

namespace DC2010
{
    public partial class Calculations
    {
	
		// Common knowledge:
		// byte1412 - Shield strength
		// Graphics.dat item 562, at location 0x1A2 (byte1412 in CSBwin), contains 6 multipliers for the strength of the shield to protect various locations: 
		// off hand, weapon hand, head, body, legs, and feet. 
		// By default, the values are 05 05 04 06 03 01 but increasing them will make shields protect each location better, 
		// so custom dungeon designers do have a way to help shields if they want to. 
		// These bytes are rather vaguely stated in the current version of the Encyclopedia as "resistances to ouches," 
		// so hopefully this description will help shed some light.
		
		
		
        public static int[] MostRecentlyAdjustedSkills = { 0, 0 }; //Just for Player display
        public static int[] LatestSkillValues = { 0, 0 }; //Just for Player display.
        public static int AdjustedSkillNumber = 0;
        public static string[] Pointer16596 = { "FIGHTER", "NINJA", "PRIEST", "WIZARD" };//"FIGHTER", "NINJA", etc
        public static int MAXHP = 999;
        public static int MAXSTAMINA = 9999;
        public static int MAXMANA = 900;
        public static int attackDamageToDisplay = 0;



        //*********************************************************
        // Called when we hit monster with sword
        //*********************************************************
        //  TAG00f930
        public int DeterminePhysicalAttackDamage(
                      ATTACKPARAMETERS pParam,
                      int monsterPosIndex,
                      int P7,
                      int P8,
                      bool vorpalOrDisrupt)
        {
            int D0L, D0W, D1L, D1W, D3L, D3W, D4L, D4W, D6L, D6W, D7L, D7W;
            CreatureData pchA3 = null;
            CreatureData objMon; //monster im attacking...
            //DB4          *DB4A2;
            CreatureInfo pmtDesc;
            ItemData objNI_attackWeapon;
            //i16           w_2;
            bool luckyHit = false;
            int throwingDistance;
            //i32           StaminaDec;
            int ranResult;
            int levelDifficulty;

            PartyData p = Root.I.GameSystem.Party;
            Layer l = Root.I.GameSystem.CurrentLayer;
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            //kdo utoci
            CreatureData cd = Root.I.GameSystem.Party.GetCreatureByIndex(pParam.charIdx);
            pchA3 = cd;

            //pchA3 = Root.I.CreatureSystem.GetData(pParam.charIdx);// & d.CH16482[pParam->charIdx]);
            objMon = Root.I.CreatureSystem.GetData(pParam.monsterUnderAttack);
            //objMon.ConstructFromInteger(pParam->monsterUnderAttack);
            //DB4A2 = pMonster;
            //if (pParam.charIdx >= d.NumCharacter)
            //{
            //    PhysicalAttackFilter(pParam);
            //    return 0;
            //};

            if (pchA3.Attributes.Health.Current == 0)
            {
                PhysicalAttackFilter(pParam);
                return 0;
            };
            levelDifficulty = l.ExperienceMultiplier;

            pmtDesc = Root.I.CreatureSystem.GetInfo(objMon.InfoId);
            objNI_attackWeapon = pchA3.Inventory.GetAttackingWeapon();
            // We modified the calling sequence to separate the
            // vorpalOrDisrupt parameter from parameter P7
            if ((!pmtDesc.Flags.IsNonMaterial) || vorpalOrDisrupt)
            {

                D0L = Quickness(pchA3);
                D1L = STRandom(32);
                D3W = 0;// pmtDesc->uByte8[4];
                D1W = 0;
                D0W = 0;

                D1W = (D1W + D3W + levelDifficulty - 16);

                if ((D0W <= D1W)       // if quickness too small
                    && ((luckyHit = (STRandom0_3() == 0)) == false)   // and no luck
                    && !IsCharacterLucky(pchA3, 75 - P7))
                {
                    //AdjustStamina(pParam->charIdx, StaminaDec=STRandomBool() + 2);
                    pParam.attdep.physicalAttack.staminaAdjust = 2 + STRandomBool();
                    PhysicalAttackFilter(pParam);
                    //DrawCharacterState(pParam->charIdx);
                    return 0;
                };

                // It is good to get here, I think
                throwingDistance = DetermineThrowingDistance(pchA3, pParam.charIdx, 1);
                D7W = (throwingDistance);

                if (throwingDistance != 0)  /////goto tag00faa2;
                {
                    D7W = (D7W + (STRandom() & 0xffff) % (D7W / 2 + 1));
                    D7W = (D7W * P8 / 32);
                    D4W = ((ranResult = STRandom(32)) + /*pmtDesc->uByte8[0]*/ +levelDifficulty);

                    if (pchA3.Inventory.GetAttackingWeapon().InfoId == "ITEM_DIAMOND_EDGE")
                    {
                        D4W = (D4W - D4W / 4);
                    }
                    else
                    {
                        if (pchA3.Inventory.GetAttackingWeapon().InfoId == "ITEM_EXECUTIONER")
                        {
                            D4W = (D4W - D4W / 8);
                        }
                    }
                    // D4 is perhaps like the monster's Armor effectiveness???
                    D7W = (D7W + (ranResult = STRandom(32)) - D4W);
                    D6W = D7W;  // chance of hitting?????
                }
                else
                {//  }}}}}}
                    D6W = 0; //Added 23Jun2004.  Tiggy 'Bashing' with a stone 
                    //club resulted in throwingDistance==0 and D6W 
                    //never got set.
                }
                if ((throwingDistance == 0) || (D6W <= 1))
                {

                    D7W = STRandom0_3();
                    if (D7W == 0)
                    {
                        //AdjustStamina(pParam->charIdx, StaminaDec=STRandomBool() + 2);
                        pParam.attdep.physicalAttack.staminaAdjust = 2 + STRandomBool();
                        PhysicalAttackFilter(pParam);
                        //DrawCharacterState(pParam->charIdx);
                        return 0;
                    };

                    D1W = (STRandom(16));
                    //ASSERT(D6L != 0x1ccccccc);
                    D6W = (D6W + D1W);
                    if ((D6W > 0)
                        || (STRandomBool() != 0))
                    {

                        D7W = (D7W + (ranResult = STRandom0_3()));
                        if (STRandom0_3() == 0)
                        {

                            D7W = (D7W + Larger(0, (ranResult = STRandom(16)) + D6W));
                        }
                    }
                }
                D7W /= 2;

                if (D7W == 0) D0W = 0;
                else D0W = ((STRandom() & 0xffff) % D7W);

                D1W = (ranResult = STRandom0_3());
                D7W = (D7W + D1W + D0W);

                if (D7W != 0) D7W = (D7W + (ranResult = (STRandom() & 0xffff) % D7W));

                D7W /= 4;
                D7W = (D7W + (ranResult = STRandom0_3()) + 1);


                //
                if ((pchA3.Inventory.GetAttackingWeapon().InfoId == "ITEM_VORPAL_BLADE")
                    && (!pmtDesc.Flags.IsNonMaterial)
                    && ((D7W = (D7W / 2)) == 0))
                {

                    //AdjustStamina(pParam->charIdx, StaminaDec=STRandomBool() + 2);
                    pParam.attdep.physicalAttack.staminaAdjust = 2 + STRandomBool();
                    PhysicalAttackFilter(pParam);
                    //DrawCharacterState(pParam->charIdx);
                    return 0;
                };
                D0W = ((ranResult = STRandom(64)));
                D1W = (DetermineMastery(pchA3, pParam.charIdx, pParam.skillNumber));
                if (D0W < D1W)
                {
                    D7W += 10;
                };
                if (D7W < 0) D7W = 0;
                pParam.attdep.physicalAttack.monsterDamage = D7W;
                //w_2 = DamageMonster(GetRecordAddressDB4(objMon), 
                //                    monsterPosIndex, 
                //                    pParam->attackX, 
                //                    pParam->attackY, 
                //                    D7W, 
                //                    1, 
                //                    false);
                D0W = 0;// ((D7W * pmtDesc->word16_8_11() / 16) + 3);

                //AdjustSkills(pParam->charIdx, pParam->skillNumber, D0W);
                pParam.attdep.physicalAttack.skillAdjust = D0W;

                //AdjustStamina(pParam->charIdx, (ranResult=STRandom0_3()) + 4);
                pParam.attdep.physicalAttack.staminaAdjust = 4 + STRandom0_3();

                PhysicalAttackFilter(pParam);
            }
            else
            { // Non-material and Not (Vorpal or Disrupt)
                D7W = 0;
                pParam.attdep.physicalAttack.monsterDamage = D7W; //Should already be zero but
                //let us be explicit.
                //w_2 = 0;
                //AdjustStamina(pParam->charIdx, (ranResult=STRandomBool()) + 2);
                pParam.attdep.physicalAttack.staminaAdjust = 2 + STRandomBool();

                PhysicalAttackFilter(pParam);
            };
            //DrawCharacterState(pParam->charIdx);

            return pParam.attdep.physicalAttack.monsterDamage;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="attackType">http://dmweb.free.fr/?q=node/690
        /// 13 - swing
        /// 14 - stab
        /// ...
        /// </param>
        /// <param name="skillNumber">http://dmweb.free.fr/?q=node/691
        /// 04: Swing (Hidden Fighter skill)
        /// 05: Thrust (Hidden Fighter skill)
        /// 06: Club (Hidden Fighter skill)
        /// 07: Parry (Hidden Fighter skill)
        /// 08: Steal (Hidden Ninja skill)
        /// 09: Fight (Hidden Ninja skill)
        /// 10: Throw (Hidden Ninja skill)
        /// 11: Shoot (Hidden Ninja skill)
        /// 12: Identify (Hidden Priest skill)
        /// 13: Heal (Hidden Priest skill)
        /// 14: Influence (Hidden Priest skill)
        /// 15: Defend (Hidden Priest skill)
        /// 16: Fire (Hidden Wizard skill)
        /// 17: Air (Hidden Wizard skill)
        /// 18: Earth (Hidden Wizard skill)
        /// 19: Water (Hidden Wizard skill)
        /// </param>
        //*********************************************************
        //
        //*********************************************************
        //  TAG01bc2a
        public int AttackWithPhysicalForce(
            CreatureData attacker,
            PartyData attackedParty,
                      ATTACKPARAMETERS pParam)
        {//(i16)
            int D0L, D0W, D6L, D6W, D7L, D7W;
            //i32 attackedMonsterOrdinal;
            object objNID0;
            bool vorpalOrDisrupt;
            D6L = 0xccccc;
            //if (traceID!=NULL)
            //{
            //  fprintf(GETFILE(TraceFile),
            //          "%sEnter AttackWithPhysicalForce (attackType=%d, skillNumber=%d)\n",
            //          traceID, pParam->attackType, pParam->skillNumber);
            //};
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            //QueueSound(16, d.partyX, d.partyY, 1);
            //if (d.MonsterUnderAttack == RNeof)
            //{
            //  if (traceID!=NULL)
            //  {
            //    fprintf(GETFILE(TraceFile),"%sNo monster in attacked square.  Return 0\n",traceID);
            //  };
            //  PhysicalAttackFilter(pParam, pFilter, traceID);
            //  return 0;
            //};
            pParam.attdep.physicalAttack.monsterDamage = 0;
            pParam.attdep.physicalAttack.staminaAdjust = 0;
            pParam.attdep.physicalAttack.skillAdjust = 0;
            //pParam.attdep.physicalAttack.attackedMonsterOrdinal = DetermineAttackOrdinal(
            //                            pParam->attackX, pParam->attackY,
            //                            d.partyX, d.partyY,
            //                            d.CH16482[pParam->charIdx].charPosition);
            //if (pParam.attdep.physicalAttack.attackedMonsterOrdinal==0)
            //{
            //  //if (traceID!=NULL)
            //  //{
            //  //  fprintf(GETFILE(TraceFile),"%sNo monster can be attacked. Return 0.\n",traceID);
            //  //};
            //  PhysicalAttackFilter(pParam);
            //  return 0;
            //};

            //D0W = character formation in party?
            D0W = 0;// ((d.CH16482[pParam->charIdx].charPosition + 4 - d.CH16482[pParam->charIdx].facing) & 3);
            D6W = 0;
            switch (D0W)
            {
                case 2:
                    D6W = 2;
                    // Note fallthrough
                    break;
                case 3:
                    D6W++;
                    D0W = 0;// (CharacterAtPosition((d.CH16482[pParam->charIdx].charPosition + D6W) & 3));
                    if (D0W != -1)
                    {
                        attackDamageToDisplay = -1; //Can't Reach
                        //if (traceID!=NULL)
                        //{
                        //  fprintf(GETFILE(TraceFile),"%sAttempt to attack through party member.  Return 0\n",traceID);
                        //};
                        PhysicalAttackFilter(pParam);
                        return 0;

                    };
                    break;
                    ;
            }; // switch (D0W)


            if (pParam.attackType.Method == "DISRUPT")
            {
                CreatureData cdAttacked = attackedParty.L.FindFirstMonster(attackedParty.Location.X, attackedParty.Location.Y);
                CreatureInfo ci = Root.I.CreatureSystem.GetInfo(cdAttacked.InfoId);
                if (!ci.Flags.IsNonMaterial)
                {
                    //if (traceID!=NULL)
                    //{
                    //  fprintf(GETFILE(TraceFile),
                    //        "%sDISRUPT but monsterDesc bit 6 word2 = 0. Return 0\n",traceID);
                    //};
                    //PhysicalAttackFilter(pParam);
                    return 0;
                };
            };
            D6W = pParam.attackType.HitProbability;
            D7W = pParam.attackType.Damage;
			
            ItemData id = attacker.Inventory.GetAttackingWeapon();

            vorpalOrDisrupt = false;
            if (id != null)
            {
                if ((id.InfoId == "ITEM_VORPAL_BLADE") || (pParam.attackType.Method == "DISRUPT"))
                {
                    vorpalOrDisrupt = true;
                    //if (traceID!=NULL)
                    //{
                    //  fprintf(GETFILE(TraceFile),"%sVorpalBlade or DISRUPT.\n",traceID);
                    //};
                }
            }
            //if (traceID!=NULL)
            //{
            //  fprintf(GETFILE(TraceFile), "%sCalling DeterminePhysicalAttackDamage(character, monster, D5W-1, D6W, D7W, skillNumber)\n",traceID);
            //};
            D0L = DeterminePhysicalAttackDamage(
                      pParam,
                //pFilter,
                //&d.CH16482[pParam->charIdx],
                //pParam->charIdx,
                //GetRecordAddressDB4(d.MonsterUnderAttack),
                      (pParam.attdep.physicalAttack.attackedMonsterOrdinal - 1),
                //pParam->attackX,
                //pParam->attackY,
                      D6W,
                      D7W,
                //pParam->skillNumber,
                      vorpalOrDisrupt);
            attackDamageToDisplay = D0L;
            //if (traceID!=NULL)
            //{
            //  fprintf(GETFILE(TraceFile),"%sWord20264 = Result of calling DeterminePhysicalAttackDamage = %d\n", traceID, D0W);
            //  fprintf(GETFILE(TraceFile),"%sReturn 1\n",traceID);
            //};
            return (1);
        }


        public static int Quickness(CreatureData cd)
        {
            int quickness, loLimit, hiLimit, ranResult;

            ranResult = Root.I.Rnd.Next(8);

            // quickness = pChar->Attributes[Dexterity].Current()
            // +(ranResult = STRandom(8));
            quickness = cd.Attributes.Dexterity.Current + ranResult;

            // D0L = (quickness/2) * pChar->load;
            int D0L = (quickness / 2) * cd.Load;

            int D1L = MaxLoad(cd);

            quickness = quickness - (D0L / D1L);

            //TODO:: Party sleeping
            //if (d.PartySleeping != 0)
            //{
            //    quickness /= 2;
            //};

            quickness /= 2;

            loLimit = Root.I.Rnd.Next(8) + 1;
            hiLimit = 100 - Root.I.Rnd.Next(8);
            quickness = ApplyLimits(loLimit, quickness, hiLimit);

            return quickness;
        }

        /// <summary>
        /// Return Max load
        /// </summary>
        /// <param name="cd"></param>
        /// <returns></returns>
        public static int MaxLoad(CreatureData cd)
        {
            int maxLoad = 8 * cd.Attributes.Strength.Current + 100;
            maxLoad = StaminaAdjustedAttribute(cd, maxLoad);
            //!!!

            //D6W = pChar->ouches;
            int D6W = cd.OuchesCount;

            if (D6W > 0)
            {
                int D0L = maxLoad;
                int D1W = (D6W & 16);
                if (D1W > 0)
                {
                    D1W = 2;
                }
                else
                {
                    D1W = 3;
                }
                maxLoad = maxLoad - (D0L >> D1W);
            }

            //boots of speed ???
            //objNID0 = pChar->Possession(5).NameIndex(); // feet??
            //if (objNID0 == objNI_ElvenBoots)
            //{
            //    maxLoad = sw(maxLoad + (maxLoad >> 4));
            //};

            maxLoad += 9;
            int D0W = (maxLoad & 0xffff) % 10;
            maxLoad = (maxLoad - D0W); // Round down to multiple of 10;
            return maxLoad;
        }

        public static int StaminaAdjustedAttribute(CreatureData cd, int baseValue)
        {
            int D0, D1, D2, D6, D7;
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            int D7W = cd.Attributes.Stamina.Current;
            int D6W = cd.Attributes.Stamina.Max / 2;
            int D0W = 0;
            if (D7W < D6W) // If below half stamina
            {
                D0W = baseValue / 2;
                baseValue = D0W;
                int D1L = baseValue;
                int D2L = D7W;
                D1L = D1L * D2L;
                D1L = D1L / D6W;
                D0W = D0W + D1L;
            }
            else
            {
                D0W = baseValue;
            }
            return D0W;
        }

        public static int ApplyLimits(int min, int x, int max)
        {
            if (x <= min) x = min;
            else if (x > max) x = max;
            return x;
        }

        public static bool IsCharacterLucky(CreatureData cd, int luckNeeded)
        {
            int D0W;
            bool result;
            int currentLuck;
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            //D7W = sw(P2);
            if (Root.I.Rnd.Next(1) == 1 && (Root.I.Rnd.Next(100) > luckNeeded))
            {
                return true;
            }

            currentLuck = cd.Attributes.Luck.Current;
            if (currentLuck == 0)
                D0W = 0;
            else
                D0W = (Root.I.Rnd.Next(cd.Attributes.Luck.Current));

            result = D0W > luckNeeded;

            D0W = ApplyLimits(cd.Attributes.Luck.Min,
                               cd.Attributes.Luck.Current + (result ? -2 : +2),
                               cd.Attributes.Luck.Max);

            return result;
        }

        public void PhysicalAttackFilter(ATTACKPARAMETERS pParam)
        {
            MONSTER_DAMAGE_RESULT monsterDamageResult;
            CreatureData objMon;
            pParam.dataType = ATTACKDATATYPE.ADT_Physical;
            //CallAttackFilter(pFilter, pParam, 1);

            //Are we attacking monster?
            if (string.IsNullOrEmpty(pParam.monsterUnderAttack)) return;

            //Get what monster we are attacking...
            objMon = Root.I.CreatureSystem.GetData(pParam.monsterUnderAttack);
            if (pParam.attdep.physicalAttack.staminaAdjust != 0)
            {
                AdjustStamina(objMon, pParam.attdep.physicalAttack.staminaAdjust);
            }
            if (pParam.attdep.physicalAttack.skillAdjust != 0)
            {
                AdjustSkills(objMon, pParam.charIdx,
                           pParam.skillNumber,
                           pParam.attdep.physicalAttack.skillAdjust,
                           ADJUSTSKILLSWHY.ASW_PhysicalAttack);
            }
            monsterDamageResult = DamageMonster(objMon,
                                pParam.attdep.physicalAttack.attackedMonsterOrdinal - 1, //monsterPosIndex, 
                                pParam.attackX,
                                pParam.attackY,
                                pParam.attdep.physicalAttack.monsterDamage, //D7W, 
                                1,
                                false);

            if (monsterDamageResult != MONSTER_DAMAGE_RESULT.NO_MONSTER_DIED) // If one or more of the monster group died.
            {
                pParam.activateMonster = 1;
                //ProcessTimers29to41(pParam->attackX, 
                //                    pParam->attackY, 
                //                    TT_M1, 
                //                    0);
            };

        }

        //*********************************************************
        //
        //*********************************************************
        //  TAG00b73a
        MONSTER_DAMAGE_RESULT DamageMonster(CreatureData pMonster,   //8
                                            int indexInGroup,//12
                                            int attackX,     //14
                                            int attackY,     //16
                                            int damage,          //18
                                            int P6,  //20
                                            bool noItem16
            //,MMRECORD* pmmr
            )
        {//(i16)
            //Return ALL_MONSTERS_DIED if last monster in group died; 
            //  else return SOME_NOT_ALL_MONSTERS_DIED if any monster in group died;
            //    else return NO_MONSTER_DIED
            int D0L, D0W, D1L, D1W, D5L, D5W, D6W, D6L, D7L, D7W;
            MONSTER_DAMAGE_RESULT monsterDamageResult;
            int d4Index;
            CreatureInfo pmtDesc;
            int w_20;
            bool onPartyLevel; //w_18;
            int w_16;
            int w_14;
            //MONSTERTYPE mt_12;
            CreatureData mt_12;

            int w_10;
            //ITEM16      *pi16_8=NULL;
            //TIMER       *pTimer_4;
            //TIMERTYPE   tt;
            bool showDeathCloud = true;
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            D7W = attackX;
            D6W = attackY;
            //mt_12 = pMonster->monsterType();
            pmtDesc = Root.I.CreatureSystem.GetInfo(pMonster.InfoId);// &d.MonsterDescriptor[mt_12];
            if (pmtDesc.Flags.IsInvincible) return MONSTER_DAMAGE_RESULT.NO_MONSTER_DIED;
            //if (pMonster.Attributes.Health.Current > 0) return MONSTER_DAMAGE_RESULT.NO_MONSTER_DIED;
            D0W = pMonster.Attributes.Health.Current;

            if (D0W <= damage)
            {  //An individual monster has died.
                w_14 = 0;// MonsterPositionByte(pMonster, d.LoadedLevel, noItem16);
                if (w_14 == 255)
                {
                    D0W = 255;
                }
                else
                {
                    //      D0W = (I16)((w_14 >> 2*indexInGroup) & 3);
                    D0W = 0;// (I16)(TwoBitNibble(w_14, indexInGroup));
                };
                w_20 = D0W;
                w_10 = 0;// pMonster.numMonM1(); // #monsters-1
                if (w_10 == 0)
                { // It was the last monster in a group of monsters
                    if (P6 != 0)
                    {
                        DELETEMONSTERPARAMETERS dmp = new DELETEMONSTERPARAMETERS(DELETEMONSTERWHY.DMW_Damage, -1);
                        //ProcessMonsterDeleteFilter(D7W, D6W, &dmp, -1);
                        if (dmp.deathCloud == 2) showDeathCloud = false;
                        //DropMonsterRemains(D7W,D6W,FindFirstMonster(D7W, D6W),2,d.LoadedLevel, dmp.dropResidue<2);
                        //DeleteMonster(D7W, D6W, pmmr);
                    };
                    monsterDamageResult = MONSTER_DAMAGE_RESULT.ALL_MONSTERS_DIED; //Group died.
                }
                else
                { //Another monster in the group still survives.
                    DELETEMONSTERPARAMETERS dmp = new DELETEMONSTERPARAMETERS(DELETEMONSTERWHY.DMW_Damage, indexInGroup);
                    //ProcessMonsterDeleteFilter(D7W, D6W, &dmp, -1);
                    if (dmp.deathCloud == 2) showDeathCloud = false;
                    w_16 = 0;// MonsterFacingByte(pMonster, d.LoadedLevel, noItem16);
                    if ((pmtDesc.Flags.IsDroppingItems) && (pmtDesc.Size == 2))
                    {
                        if (P6 != 0)
                        {
                            if (dmp.dropResidue != 2)
                            {
                                //CreateAndDropObject(mt_12, D7W, D6W, w_20, 2);
                            };
                        }
                        else
                        {
                            //d.Byte13050[d.Word13046++] = (i8)w_20;
                        };
                    };



                    onPartyLevel = Root.I.GameSystem.Party.LayerId == Root.I.GameSystem.CurrentLayer.Id;
                    if (onPartyLevel)
                    {
                        //ASSERT(pMonster->groupIndex() < d.MaxITEM16,"maxitem16");
                        //pi16_8 = 0;//&d.Item16[pMonster->groupIndex()];
                    };
                    if (pMonster.Fear == STATE_OF_FEAR.StateOfFear6)
                    {
                        if (onPartyLevel)
                        {
                            int w_12;
                            w_12 = pmtDesc.Bravery;
                            if (w_12 != 15) // 15 is fearless!
                            {
                                w_12 += w_10 - 1;
                                D1W = STRandom(16);
                                if (w_12 < D1W)
                                {
                                    D1W = (100 - 4 * w_12);
                                    D0W = (STRandom(D1W) + 20);
                                    //pi16_8->uByte5 = D0UB;
                                    //30OCT02SETWBITS0_3(pMonster->word14, 5);
                                    pMonster.Fear = STATE_OF_FEAR.StateOfFear5;
                                };
                            };
                        };
                    };
                    d4Index = (indexInGroup);
                    D5W = 0;
                    for (D5L = d4Index; D5W < w_10; D5W++)
                    {
                        d4Index++;
                        //pMonster->hitPoints6[D5W] = pMonster->hitPoints6[d4Index];
                        //        w_16 = SetMonsterPositionBits(w_16, D5W, (w_16 >> 2*D4W) & 3);
                        w_16 = 0;//SetMonsterPositionBits(w_16, D5W, TwoBitNibble(w_16,d4Index));
                        //        w_14 = SetMonsterPositionBits(w_14, D5W, (w_14 >> 2*D4W) & 3);
                        w_14 = 0;//SetMonsterPositionBits(w_14, D5W, TwoBitNibble(w_14,d4Index));
                        if (onPartyLevel)
                        {
                            //pi16_8->singleMonsterStatus[D5W] = pi16_8->singleMonsterStatus[d4Index];
                        };

                        //
                    };
                    w_14 &= 0x3f;
                    //SetMonsterPositionByte(pMonster, w_14, d.LoadedLevel, noItem16);
                    //SetMonsterFacingByte(pMonster, w_16, d.LoadedLevel, noItem16);
                    // Decrement monsters remaining.
                    //30OCT02SETWBITS5_6(pMonster->word14, (BITS5_6(pMonster->word14)-1)&3);
                    //pMonster->numMonM1(pMonster->numMonM1()-1);
                    monsterDamageResult = MONSTER_DAMAGE_RESULT.SOME_NOT_ALL_MONSTERS_DIED;
                };
                D5W = pmtDesc.Size;
                if (D5W == 0)
                {
                    D5W = 110;
                }
                else
                {
                    if (D5W == 1)
                    {
                        D5W = 190;
                    }
                    else
                    {
                        D5W = 255;
                    };
                };
                if (showDeathCloud)
                {
                    //if (pMonster->drawAsSize4() && drawAsSize4Monsters)
                    //{
                    //  if (w_10 == 0)
                    //  {
                    //    CreateCloud(RN(RNMonsterDeath), 255, D7W, D6W, D7W, D6W, 255, pmmr);
                    //  };
                    //}
                    //else
                    //{
                    //  CreateCloud(RN(RNMonsterDeath), D5W, D7W, D6W, D7W, D6W, w_20, pmmr);
                    //};
                };
                return monsterDamageResult;
            }

            if (damage > 0)
            {
                pMonster.Attributes.Health.Dec(damage);
            }
			
            return MONSTER_DAMAGE_RESULT.NO_MONSTER_DIED;
        }

        /// <summary>
        /// 100% - This works same way as CSBWin.
		/// This method sets new stamina and in case that stamina is negative
		/// Health of player is decresased.
		/// </summary>
        public static void AdjustStamina(CreatureData pChar, int decrement)
        {
            int absStamina;
            int newStamina;

            newStamina = pChar.Attributes.Stamina.Current - decrement;

            if (newStamina <= 0)
            {
                absStamina = -newStamina;
                pChar.Attributes.Stamina.Current = 0;
                Calculations.DamageCharacter(pChar, absStamina / 2, 0, 0);
            }
            else
            {
                if (newStamina > pChar.Attributes.Stamina.Max)
                {
                    pChar.Attributes.Stamina.SetCurrent(pChar.Attributes.Stamina.Max);
                }
                else
                {
                    pChar.Attributes.Stamina.SetCurrent(newStamina);
                }
            }

            pChar.CharFlags |= CHARFLAGS.CHARFLAG_possession | CHARFLAGS.CHARFLAG_statsChanged;
        }

        /// <summary>
        /// 0% - This works same way as CSBWin.
		/// This method sets new stamina and in case that stamina is negative
		/// Health of player is decresased.
		/// </summary>
		/// <param name="pChar">Creature i wish to harm.</param>
		/// <param name="damage">?</param>
		/// <param name="Mask">?</param>
		/// <param name="P4">?</param>
        public static int DamageCharacter(CreatureData pChar, int damage, int mask, int P4)
        {
            PartyData p = Root.I.GameSystem.Party;

            int D0L, D0W, D1L, D1W, D1UW, D3L, D3W, D4L, D4W, D5L, D5W, D6L, D6W, D7L;

            int LOCAL_30;
            int LOCAL_20;
            int LOCAL_10;
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

            int D7W = damage;

			// ??? tohle nevim k cemu je
			// Word11694
			//  i16 Word11694; //Set when game is won.
			//				 //This stops monster movement, prevents
			//				 //processing of Timer function 24, and
			//				 //stops any damage to characters.			
            //if (d.Word11694 != 0) return d.Word11694;
			
			//Rasmus: http://dungeon-master.com/forum/viewtopic.php?f=46&t=28401&p=103982&hilit=TAG016426#p103961
		   //if (includehurt)
		   //{
			//  int damageparts[6]={15, 15, head, torso, legs, feet};
			 // if (damageall) for (int i=0;i<6;i++) damageparts[i]=0;
			  //int maxhealth=sHero[herocount].stats[0][1];
			  //for (int i=0;i<6;i++)
			 // {
				// damage=D7W-int(float(float(D7W)*(float(damageparts[i])/15.0f)));
			//	 damage=int(damage/3);
				// int hurtprob=maxhealth-damage;
			//	 if (hurtprob<int(maxhealth-int(maxhealth/10)))
				// {
					//if ((int(rand() % hurtprob)<=int(maxhealth/10))||(hurtprob<0))
			//		{
				//	   sHero[herocount].hurt[i]+=20+damage;
					//}
		//		 }
			//  }
		//   }			
			
			
			// There is no damage to give to monster....quit
            if (D7W <= 0) return 0;

			// Neni co ubirat. Hrac je pravdepodobne mrtvy.
            if (pChar.Attributes.Health.Current == 0) return 0;
			
            if (P4 != 0)
            {
                for (D4W = D5W = D6W = 0; D6W <= 5; D6W++)
                {
                    if ((mask & (1 << D6W)) == 0) continue;
                    D4W++;
                    D0W = D6W;
                    if (P4 == 4)
                    {
                        D1UW = 0x8000;
                    }
                    else
                    {
                        D1W = 0;
                    };
                    //D5W = D5W + TAG01680a(chIdx, D0W | D1W);

                    //
                }
				
                if (D4W > 0)
                {
                    D5W = (D5W & 0xffff) / (D4W & 0xffff); // Average
                }
				
                if (P4 == 6)
                {
                    D0W = 115;
                    D3W = (pChar.Attributes.Wisdom.Current);
                    D6W = D0W - D3W;
					
                    if (D6W <= 0)
                    {
						// D7W drzi damage, kterym se ma hitnout
                        D7W = 0;
                    }
                    else
                    {
						// Zvysi damage????
                        D7W = ScaledMultiply(D7W, 6, D6W);//D6*D7/64
                    };
                }
                else
                {
                    if (P4 == 5)
                    {
						// Uprav AntiMagic
                        //D7W = TAG016426(pcA3, 5, D7W);
                        //D7W = sw(D7W - d.SpellShield);
                    }
                    else
                    {
                        if (P4 == 1)
                        {
							// Uprav AntiFire
                            //D7W = TAG016426(pcA3, 6, D7W);
                            //D7W = sw(D7W - d.FireShield);
							
							// Sophia: http://dungeon-master.com/forum/viewtopic.php?f=28&t=28388&p=103557&hilit=TAG016426#p103557
							// damage = ((200 - Anti_Fire) * base_damage) / 128
							// if (base_damage > (damage * 5)) then
							// damage = base_damage / 5
                        }
                        else
                        {
                            if (P4 == 2)
                            {
                                D5W >>= 1;
                            }
                        };
                        if (D7W <= 0) return 0;

                        D0W = 130 - D5W;
                        D7W = ScaledMultiply(D7W, 6, D0W); //D6*D7/64
                    };
                };
                if (D7W <= 0) return 0;
                D0W = Root.I.Rnd.Next(128) + 10;
				// Uprav vitality
                //D6W = TAG016426(pcA3, 4, D0W);
                LOCAL_10 = D6W;
                if (D7W > LOCAL_10)
                {
                    do
                    {
                        LOCAL_20 = Root.I.Rnd.Next(8);
                        D1W = (1 << LOCAL_20) & mask;
                        //d.PendingOuches[chIdx] |= D1W;
                        D6W <<= 1;
                        LOCAL_30 = D6W;
                        if (D7W <= LOCAL_30) break;
                    } while (D6W != 0);
                };
                if (p.IsPartySleeping)
                {
                    p.L.WakeUp();
                };
            };
            if (D7W <= 0)
            {
                D7W = 0;
            };


            //p.PendingDamage[chIdx] = p.PendingDamage[chIdx] +D7W;
            return D7W;
        }

        //*********************************************************
        //
        //*********************************************************
        public int TAG016426(CreatureData pChar, int attrNum, int P3)
        {
            int D0L, D0W, D7L, D7W;
            int w_10 = 0;

            //if (attrNum == 0) w_10 = pChar.Attributes.Luck.Current;
            //if (attrNum == 1) w_10 = pChar.Attributes.Strength.Current;
            //if (attrNum == 2) w_10 = pChar.Attributes.Dexterity.Current;
            //if (attrNum == 3) w_10 = pChar.Attributes.Wisdom.Current;
            //if (attrNum == 4) w_10 = pChar.Attributes.Vitality.Current;
            //if (attrNum == 5) w_10 = pChar.Attributes.AntiMagic.Current;
            //if (attrNum == 6) w_10 = pChar.Attributes.AntiFire.Current;

			w_10 = pChar.Attributes[attrNum].Current;

            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            //w_10 = 0;// pChar->Attributes[attrNum].Current();
            //
            // *************** Spohia thinks the '/256' is a bug.**********
            // I will remove it as a test to let people try the game without it.
            //
            D7W = 170 - w_10 / 256; //They stored a word and fetch a byte!
            //Appears it was purposeful!
            //D7W = sw(170 - w_10); //They stored a word and fetch a byte!
            // *************************************************************
            //
            if (D7W < 16)
            {
                D0W = P3 >> 3;
            }
            else
            {
                D0W = ScaledMultiply(P3, 7, D7W);//P3*D7/128
            };

            return D0W;
        }

        public static int ScaledMultiply(int Op1, int scale, int Op2)
        {
            return (Op1) * (Op2) >> scale;
        }


        //**********************************************************************
        // LevelUp
        // Extracted from AdjustSkills to make things easier to document
        //**********************************************************************
        public static void LevelUp(CreatureData pChar, int chIdx, int basicSkill) // Fighter, ninja, etc.
        {
            int newMastery;
            int maxManaIncrement, maxVitalityIncrement, maxStaminaIncrement = 0, maxHPIncrement;
            int antiFireIncrement;
            int majorAttributeMaxIncrement, minorAttributeMaxIncrement;
            int color;
            //
            newMastery = DetermineMastery(pChar, chIdx, basicSkill | 0xc000);
            maxManaIncrement = newMastery;
            minorAttributeMaxIncrement = STRandomBool();
            majorAttributeMaxIncrement = STRandomBool() + 1;
            maxVitalityIncrement = STRandomBool();
            if (basicSkill != 2)
            {
                maxVitalityIncrement &= newMastery;
            };
            pChar.Attributes.Vitality.IncrMaximum(maxVitalityIncrement);
            antiFireIncrement = STRandomBool() & (~newMastery);
            pChar.Attributes.AntiFire.IncrMaximum(antiFireIncrement);
            switch ((SkillNames)basicSkill)
            {
                case SkillNames.SKILL_Fighter:  // fighter
                    //D6W /= 16;
                    maxStaminaIncrement = pChar.Attributes.Stamina.Max / 16;
                    newMastery *= 3;
                    pChar.Attributes.Strength.IncrMaximum(majorAttributeMaxIncrement);
                    pChar.Attributes.Dexterity.IncrMaximum(minorAttributeMaxIncrement);
                    break;

                case SkillNames.SKILL_Ninja: // ninja
                    //D6W = sw(D6W / 21);
                    maxStaminaIncrement = pChar.Attributes.Stamina.Max / 21;
                    newMastery = 2 * newMastery;
                    pChar.Attributes.Strength.IncrMaximum(minorAttributeMaxIncrement);
                    pChar.Attributes.Dexterity.IncrMaximum(majorAttributeMaxIncrement);
                    break;
                case SkillNames.SKILL_Priest: // priest

                    maxStaminaIncrement = pChar.Attributes.Stamina.Max / 25;
                    pChar.Attributes.Mana.IncrMaximum(newMastery);
                    newMastery = newMastery + (newMastery + 1) / 2;
                    pChar.Attributes.Wisdom.IncrMaximum(minorAttributeMaxIncrement);

                    maxManaIncrement = Smaller(STRandom0_3(), maxManaIncrement - 1);
                    if (pChar.Attributes.Mana.Max + maxManaIncrement > MAXMANA)
                        pChar.Attributes.Mana.SetMax(MAXMANA);
                    else
                        pChar.Attributes.Mana.IncrMaximum(maxManaIncrement);

                    pChar.Attributes.AntiMagic.IncrMaximum(STRandom(3));
                    break;
                case SkillNames.SKILL_Wizard:

                    maxStaminaIncrement = pChar.Attributes.Stamina.Max / 32;

                    if (pChar.Attributes.Mana.Max + newMastery + newMastery / 2 <= MAXMANA)
                    {
                        pChar.Attributes.Mana.IncrMaximum(newMastery + newMastery / 2);
                    }
                    else
                    {
                        pChar.Attributes.Mana.SetMax(MAXMANA);
                    };

                    //increment wisdom
                    pChar.Attributes.Wisdom.IncrMaximum(majorAttributeMaxIncrement);

                    //increment mana
                    maxManaIncrement = Smaller(STRandom0_3(), maxManaIncrement - 1);
                    if (pChar.Attributes.Mana.Max + maxManaIncrement > MAXMANA)
                        pChar.Attributes.Mana.SetMax(MAXMANA);
                    else
                    {
                        pChar.Attributes.Mana.IncrMaximum(maxManaIncrement);
                    }

                    //increment antimagic
                    pChar.Attributes.AntiMagic.IncrMaximum(Root.I.Rnd.Next(3));
                    break;
            };
            maxHPIncrement = newMastery + Root.I.Rnd.Next((newMastery / 2) + 1);
            if (pChar.Attributes.Health.Max + maxHPIncrement > 999)
            {
                pChar.Attributes.Health.SetMax(999);
            }
            else
            {
                pChar.Attributes.Health.IncrMaximum(maxHPIncrement);
            };
            //w_26 = uw(STRandom(((UI16)D6W/2)+1));
            if (maxStaminaIncrement == 0) maxStaminaIncrement = 1;
            else maxStaminaIncrement += Root.I.Rnd.Next(maxStaminaIncrement / 2) + 1;
            if (pChar.Attributes.Stamina.Max + maxStaminaIncrement > 9999)
            {
                pChar.Attributes.Stamina.SetMax(9999);
            }
            else
            {
                pChar.Attributes.Stamina.IncrMaximum(maxStaminaIncrement);
            };
            pChar.CharFlags |= CHARFLAGS.CHARFLAG_statsChanged;
            //DrawCharacterState(chIdx);
            //PrintLinefeed();

            GameSystem g = Root.I.GameSystem;

            g.QuePrintLines(1, pChar.ShortName);
            g.QuePrintLines(1, " JUST GAINED A ");
            g.QuePrintLines(1, Pointer16596[basicSkill]); //"FIGHTER", "NINJA", etc
            g.QuePrintLines(1, " LEVEL");
            //***********************************************************
        }


        //
        //***********************************************************
        //   TAG01605a
        public static void AdjustSkills(CreatureData pChar, int chIdx, int skillNumber, int experience, ADJUSTSKILLSWHY why)
        {
            //(void)
            //dReg D6;
            SKILL psA3;
            CreatureData pcA2;
            //i16 w_26;
            //i16 w_6;
            //i16 w_4;
            //i16 w_2;
            int basicSkill;
            int oldMastery, newMastery;
            //NEWDSAPARAMETERS ndp;

            PartyData p = Root.I.GameSystem.Party;
            Layer l = Root.I.GameSystem.CurrentLayer;


            /////////////////////////////////////////////////////////////////
            //Now we do the actual changes to the character's stats.
            /////////////////////////////////////////////////////////////////





            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            //D7W = sw(Experience);
            if ((skillNumber >= 4) && (skillNumber <= 11))
            {
                //if no creature has attacked the party for at least 150 clock ticks (25 seconds)
                if ((p.Time - p.LastMonsterAttackTime) > 25)
                {
                    experience /= 2;
                }
            }

            //if (experience == 0) return;
            //D6W = d.pCurLevelDesc->experienceMultiplier();
            if (l.ExperienceMultiplier != 0)
            {
                experience *= l.ExperienceMultiplier;
            }

            pcA2 = pChar;// &d.CH16482[chIdx];
            if (skillNumber >= 4)
            {
                basicSkill = (skillNumber - 4) / 4;
            }
            else
            {
                basicSkill = skillNumber;
            }
            ;
            oldMastery = DetermineMastery(pChar, chIdx, basicSkill | 0xc000);
            if (skillNumber >= 4)
            {
                if (p.Time - p.LastMonsterAttackTime > 25)
                {
                    experience *= 2;
                }

            }

            psA3 = pcA2.Skills[skillNumber];


            /////////////////////////////////////////////////////
            //Now let the Adjust Skills Filter DSA do its thing
            /////////////////////////////////////////////////////
            {
                int key;

                int len;
                key = 0;//(EXPOOL_DATA_TYPE.EDT_SpecialLocations << 24) | EXPOOL_DATA_TYPE.ESL_ADJUSTSKILLSFILTER;
                len = 0;//expool.Locate(key, &pRecord);
                if (len > 0)


                    if (experience == 0) return;


                psA3.experience += (experience);

                MostRecentlyAdjustedSkills[AdjustedSkillNumber] = skillNumber;
                LatestSkillValues[AdjustedSkillNumber] = psA3.experience;
                AdjustedSkillNumber = (AdjustedSkillNumber + 1) % 2;
                if (psA3.tempAdjust < 32000)
                {
                    psA3.tempAdjust = psA3.tempAdjust + ApplyLimits(1, experience / 8, 100);
                }
                ;
                psA3 = pcA2.Skills[basicSkill];
                if (skillNumber >= 4)
                {
                    psA3.experience += (experience);
                }

                newMastery = DetermineMastery(pcA2, chIdx, basicSkill | 0xc000);
                if (newMastery <= oldMastery)
                {
                    return;
                }
                ;

                LevelUp(pcA2, chIdx, basicSkill);
            }
        }

        //  TAG015f1e
        public static int DetermineMastery(CreatureData pChar, int chIdx, int skill)
        {
            // if skill & 0x4000 then don't add adjustment for possessions.
            // if skill & 0x8000 then don't add temporary adjustment.
            int experience, mastery;
            object objNID4, objNID5; ;
            int ignorePossessions;
            int ignoreTemporaryAdjustments;
            //CreatureData pChar;
            SKILL pSkill;
            PartyData p = Root.I.GameSystem.Party;
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            if (p.IsPartySleeping)
            {
                return 1; // Report no mastery of this skill
            };
            ignoreTemporaryAdjustments = skill & 0x8000;
            ignorePossessions = skill & 0x4000;
            skill &= 0x3fff;


            //pChar = &d.CH16482[chIdx];
            pSkill = pChar.Skills[skill];
            experience = pSkill.experience;
            if (ignoreTemporaryAdjustments == 0)
            {
                experience += pSkill.tempAdjust;
            }
            if (skill > 3) // If subskill
            {
                pSkill = pChar.Skills[(skill - 4) / 4];
                experience += pSkill.experience;
                if (ignoreTemporaryAdjustments == 0)
                {
                    experience += pSkill.tempAdjust;
                }
                experience >>= 1;
            }
            for (mastery = 1; experience >= 500; mastery++)
            {
                experience >>= 1;
                //
            }

            if (ignorePossessions == 0)
            {
                if (pChar.L.CheckOwnership("ITEM_FIRESTAFF_EMPTY"))
                {
                    mastery++;
                }
                else
                {
                    if (pChar.L.CheckOwnership("ITEM_FIRESTAFF_FULL"))
                    {
                        mastery += 2;
                    };
                };

                switch ((SkillNames)skill)
                {
                    case SkillNames.SKILL_Wizard:
                        if (pChar.L.CheckOwnership("ITEM_PENDANT_FERAL"))
                        {
                            mastery++;
                        };
                        break;
                    //
                    case SkillNames.SKILL_Defend: //Priestly skill
                        if (pChar.L.CheckOwnership("ITEM_EKKHARD_CROSS"))
                        {
                            mastery++;
                        };
                        break;
                    //
                    case SkillNames.SKILL_Heal: //Priestly skill
                        if (
                            (pChar.L.CheckOwnership("ITEM_GEM_OF_AGES")) ||
                            (pChar.L.CheckOwnership("ITEM_SCREPTRE_OF_LYF"))
                            )
                        {
                            mastery++;
                        }
                        break;
                    //
                    case SkillNames.SKILL_Influence: //Priestly skill
                        if (pChar.L.CheckOwnership("ITEM_MOONSTONE"))
                        {
                            mastery++;
                        }
                        break;
                }
            }

            return mastery;
        }

        public static int Smaller(int x, int y)
        {
            return x < y ? x : y;
        }

        public static int Larger(int x, int y)
        {
            return x > y ? x : y;
        }

        public static int STRandom(int num)
        {
            if (num == 0) return 0;
            return Root.I.Rnd.Next(num); // To be exactly like Atari
        }

        /// <summary>
        /// Zero or One
        /// </summary>
        /// <returns></returns>
        public static int STRandomBool()
        {
            return Root.I.Rnd.Next(1);
        }

        public static int STRandom0_3()
        {
            return Root.I.Rnd.Next(3);
        }



        //
        //*********************************************************
        //  TAG0166b2
        public int DetermineThrowingDistance(CreatureData pChar, int chIdx, int hand)
        {
            int D0L, D0W, D1L, D1W, D3L, D3W, D5L, D5W, D6W, D6L, D7W, D7L;
            ItemData objD4;
            CreatureData pcA3;
            ItemInfo weaponDescA2;
            int result;
            int w_12;
            int w_2;
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            pcA3 = pChar;
            D3W = pcA3.Attributes.Strength.Current;
            D7W = D3W;// ((STRandom() & 15) + D3W);
            objD4 = pcA3.Inventory.GetAttackingWeapon();//->Possession(hand);
            if (objD4 == null) return 0; //neni cim hazet....
            D6L = objD4.L.GetObjectWeight();

            D5W = (MaxLoad(pcA3) / 16);
            D6W = 0;//
            if (D6W <= D5W)
            {
                D7W = (D7W + D6W - 12);
            }
            else
            {
                w_2 = ((D5W - 12) / 2 + D5W);
                w_12 = w_2;
                if (D6W <= w_12)
                {
                    D7W = (D7W + (D6W - D5W) / 2);
                }
                else
                {
                    D7W = (D7W - 2 * (D6W - w_2));
                };
            };
            if ((objD4 != null)
                 && (objD4.Type == BaseObjectType.Weapon))
            {
                //weaponDescA2 = TAG0099d2(objD4);
                D7W = 0;// sw(D7W + weaponDescA2->uByte2);

                D6W = 0;
                D5W = 0;// weaponDescA2->uByte1;

                if ((D5W == 0) || (D5W == 2))
                {
                    D6W = DetermineMastery(pChar, chIdx, 4);
                }
                if ((D5W != 0) && (D5W < 16))
                {
                    D6W = (D6W + DetermineMastery(pChar, chIdx, 10));
                }
                if ((D5W >= 16) && (D5W < 112))
                {
                    D6W = (D6W + DetermineMastery(pChar, chIdx, 11));
                }
                D7W = (D7W + 2 * D6W);
            }
            D7W = StaminaAdjustedAttribute(pcA3, D7W);

            D0W = pcA3.OuchesCount;

            if (hand == 0) D1W = 1;
            else D1W = 2;
            //
            if (D1W > 0 & D0W > 0)
            {
                D7W /= 2;
            };
            result = ApplyLimits(0, D7W / 2, 100);
            return result;
        }

        public static int STRandom() //TAG001030
        {
            int D0L, D0W;
            return Root.I.Rnd.Next(int.MaxValue);
        }



        //*********************************************************
        //
        //*********************************************************
        public int AttackWithSpell(//i32   chIdx,
            CreatureData Attacker,
            PartyData attackedParty,
                            ATTACKPARAMETERS pParam,
                            int range,
                            SpellInfo rnD5,
                            int neededMana,
                            ref int experienceGained)
        {
            int success;
            SpellData objSpell;

            // Turn attacker so he will face party
            Attacker.L.SetCharToPartyFacing(attackedParty);
            //ASSERT(D4L != 0xccccc); // Uninitialized value
            if (Attacker.Attributes.Mana.Current < neededMana)
            {
                //range = Larger(2, (d.CH16482[chIdx].Mana() * range) / neededMana);
                range = range * Attacker.Attributes.Mana.Current / neededMana;
                if (range < 2) range = 2;
                neededMana = Attacker.Attributes.Mana.Current;
            };
            //ASSERT(range != 0xccc,"range not defined"); //uninitialized value
            pParam.dataType = ATTACKDATATYPE.ADT_Spell;
            pParam.attdep.spellAttack.spellRange = range;
            pParam.attdep.spellAttack.spellType = rnD5.Id;// 0;// rnD5.ConvertToInteger();
            pParam.neededMana = neededMana;
            pParam.attdep.spellAttack.decrementCharges = 1;
            //CallAttackFilter(pFilter, pParam, 1);
            objSpell = rnD5.CreateData();// Root.I.SpellSystem.GetInfo("");// (RNVAL)pParam->attdep.spellAttack.spellType;
            success = CharacterThrowsSpell(
                          Attacker,
                          objSpell,//rnD5, 
                          pParam.attdep.spellAttack.spellRange,
                          pParam.neededMana);
            if (success == 0)
            {
                experienceGained /= 2;
            };
            if (pParam.attdep.spellAttack.decrementCharges != 0)
            {
                Attacker.L.DecrementChargesRemaining();
            };
            return success;
        }

        //
        //***********************************************************
        //  TAG0174ec
        public int CharacterThrowsSpell(CreatureData attacker, SpellData spell, int range, int neededMana)
        {//(i16)
            int D1L, D1W;
            D1W = 0;
            int decayRate;
            CreatureData pcA3;
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            //D7W = sw(P3);
            pcA3 = attacker;
            if (pcA3.Attributes.Mana.Current < neededMana)
            {
                return 0;
            }
            pcA3.Attributes.Mana.Incr(-neededMana);
            pcA3.CharFlags |= CHARFLAGS.CHARFLAG_statsChanged;
            D1L = Smaller(8, pcA3.Attributes.Mana.Max / 8);
            decayRate = (10 - D1W);
            if (range < 4 * decayRate)
            {
                range += 3;
                decayRate -= 1;
            };
            return LaunchObject(pcA3,
                                spell,
                                range,        //range
                                90,           //damage
                                decayRate);   //decayRate
        }

        public int LaunchObject(CreatureData pChar, BaseObject bo, int range, int damage, int decayRate)
        {//(i16)
            int D7L, D7W;
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

            //pd where launcherer is in
            PartyData pd = null;// Root.I.PartySystem.GetData(pChar.PartyId);


            D7W = (int)pChar.Facing;
            return 0;
            //return LaunchMissile(
            //              bo,
            //              pd.Location.X,
            //              pd.Location.Y,
            //              D7W,//((((pChar->charPosition - D7W + 1) & 2) >> 1) + D7W) & 3,//direction
            //              D7W,
            //              range,      //range
            //              damage,         //damage
            //              decayRate,  //decayRate
            //              false);
        }


        //*********************************************************
        //  case  atk_WARCRY:
        //  case  atk_CALM:
        //  case  atk_BRANDISH:
        //  case  atk_BLOWHORN:
        //  case  atk_CONFUSE
        //*********************************************************
        //  TAG01bac2
        public static int WarCryEtc(
            CreatureData Attacker, ATTACKPARAMETERS pParam,
                      int attackX,
                      int attackY)
        {//(i16)
            int D0L, D0W, D1L, D1W;
            int success;
            //i32         skillIncrement;
            //i32         requiredMastery;
            CreatureData DB4A3;
            CreatureInfo pmtDesc;
            //i32         effectiveMastery;
            //ITEM16      *pi16_4;
            int Mastery;
            int temp;
            pParam.dataType = ATTACKDATATYPE.ADT_WarCry;
            pParam.attdep.warcryetc.skillIncrement = 0xccccc;
            pParam.attdep.warcryetc.requiredMastery = 0xccccc;
            Mastery = 0xccccc;
            //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            success = 0;
            //if (d.MonsterUnderAttack == RNeof) 
            //{
            //  //CallAttackFilter(pFilter, pParam, 1); // We promised to always call
            //  return 0;
            //};
            switch (pParam.attackType.Id)
            {
                case "WARCRY":
                    {
                        pParam.attdep.warcryetc.mastery = 3;
                        pParam.attdep.warcryetc.skillIncrement = 12;
                        break;
                    }
                case "CALM":
                    {
                        pParam.attdep.warcryetc.mastery = 7;
                        pParam.attdep.warcryetc.skillIncrement = 35;
                        break;
                    }
                case "BRANDISH":
                    {
                        pParam.attdep.warcryetc.mastery = 6;
                        pParam.attdep.warcryetc.skillIncrement = 30;
                        break;
                    }
                case "BLOWHORN":
                    {
                        pParam.attdep.warcryetc.mastery = 6;
                        pParam.attdep.warcryetc.skillIncrement = 20;
                        break;
                    }
                case "CONFUSE":
                    {
                        pParam.attdep.warcryetc.mastery = 12;
                        pParam.attdep.warcryetc.skillIncrement = 45;
                        break;
                    }
            }; // switch ()
            //ASSERT(pParam->attdep.warcryetc.mastery != 0xccccc,"pParam->attdep");

            DB4A3 = Root.I.CreatureSystem.GetData(pParam.monsterUnderAttack);
            pParam.attdep.warcryetc.mastery = (pParam.attdep.warcryetc.mastery + (temp = DetermineMastery(DB4A3, pParam.charIdx, 14)));

            pmtDesc = Root.I.CreatureSystem.GetInfo(DB4A3.InfoId);
            pParam.attdep.warcryetc.effectiveMastery = ((STRandom() & 0xffff) % pParam.attdep.warcryetc.mastery);
            pParam.attdep.warcryetc.requiredMastery = pmtDesc.Bravery;

            if ((pParam.attdep.warcryetc.requiredMastery > pParam.attdep.warcryetc.effectiveMastery) || (pParam.attdep.warcryetc.requiredMastery == 15))
            {
                pParam.attdep.warcryetc.skillIncrement /= 2;
            }
            else
            {
                //pi16_4 = &d.Item16[DB4A3->groupIndex()];
                if (DB4A3.Fear == STATE_OF_FEAR.StateOfFear6)
                {
                    //ClearAttacking_DeleteMovementTimers(pi16_4, attackX, attackY);
                    //StartMonsterMovementTimers(attackX, attackY);
                };
                DB4A3.Fear = STATE_OF_FEAR.StateOfFear5;
                D0L = 0;
                D0W = (4 * (16 - pParam.attdep.warcryetc.requiredMastery));
                D1L = 0;
                //                            int D1B = pmtDesc->timePerMove();//uByte6;
                D1W = 0;
                D0W = (D0L / D1W);
                // pi16_4->uByte5 = D0B;
                success = 1;
            };
            AdjustSkills(Attacker, pParam.charIdx, (int)SkillNames.SKILL_Influence, pParam.attdep.warcryetc.skillIncrement, ADJUSTSKILLSWHY.ASW_WarCryEtc);
            return success;
        }
		
		//  TAG01657e
		public static int TimeToMove(CreatureData pChar)
		{ // Speed of character???
		  int D0L, D0W, D1L, D1W, D6L, D6W, D7L, D7W;
		  int time;
		  ItemData objNID0;
		  
		  D6L = MaxLoad(pChar);
		  D7W = pChar.Load;
		  if (D6L > D7W)
		  {
			time = 2;
			D0L = 8 * (D7W & 0xffff);
			D1L = 5 * (D6L & 0xffff);
			//D1L = 5 * D1L;
			if (D0L > D1L)
			{
			  time++;
			};
			D7W = 1;
		  }
		  else
		  {
			time = 4;
			D1L = (4 * (D7W - D6L)) & 0xffff;
			time = time + D1L / D6L;
			D7W = 2;
		  };

            // co tohle asi dela?
		  if ((pChar.OuchesCount & 32)==0)
		  {
			time = time + D7W;
		  };
		  string botyId = pChar.Inventory.GetItemAtPosition(CarryLocations.Feet);//  ->Possession(5).NameIndex(); // feet
          ItemData id = Root.I.ItemSystem.GetData(botyId);

		  if ((id!=null)&&(id.InfoId == "ITEM_BOOTS_OF_SPEED"))
		  {
			time--;
		  }

		  return time;
		}		
    }
}
